#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
配送路径优化系统 - Delivery Route Optimization System

功能包括：
- 多约束路径优化算法
- 实时交通信息集成
- 配送员工作负载平衡
- 配送效率分析报告
"""

import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
import json
import logging
from dataclasses import dataclass, asdict
from enum import Enum
import math
import random
from collections import defaultdict

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class VehicleType(Enum):
    """车辆类型枚举"""
    MOTORCYCLE = "motorcycle"  # 摩托车
    VAN = "van"                # 面包车
    TRUCK = "truck"            # 卡车
    ELECTRIC = "electric"      # 电动车

class DeliveryStatus(Enum):
    """配送状态枚举"""
    PENDING = "pending"        # 待配送
    ASSIGNED = "assigned"      # 已分配
    IN_PROGRESS = "in_progress" # 配送中
    DELIVERED = "delivered"    # 已送达
    FAILED = "failed"          # 配送失败
    RETURNED = "returned"      # 已退回

@dataclass
class Location:
    """位置信息"""
    id: str
    name: str
    latitude: float
    longitude: float
    address: str
    is_warehouse: bool = False
    
    # TODO: [智能位置管理] - 扩展位置信息和地理智能分析
    # 业务需求:
    # 1. 支持位置聚类分析和配送区域划分
    # 2. 实现位置访问频率统计和热点分析
    # 3. 建立位置风险评估（交通、安全、天气）
    # 4. 支持动态位置更新和地址标准化
    # 5. 实现位置间距离和时间的精确计算
    # 技术实现:
    # 1. 集成地理编码API实现地址标准化
    # 2. 使用聚类算法进行配送区域优化
    # 3. 建立位置画像和访问模式分析
    # 4. 集成实时交通和路况数据

@dataclass
class Vehicle:
    """配送车辆信息"""
    vehicle_id: str
    vehicle_type: str
    capacity_weight: float  # 载重能力(kg)
    capacity_volume: float  # 载货体积(m³)
    max_distance: float     # 最大行驶距离(km)
    cost_per_km: float      # 每公里成本
    driver_id: str
    current_location: str
    is_available: bool = True
    working_hours: Tuple[int, int] = (8, 18)  # 工作时间(小时)
    
    # TODO: [智能车辆调度] - 实现车辆资源优化和动态调度
    # 业务需求:
    # 1. 建立车辆负载均衡和效率优化算法
    # 2. 实现车辆维护计划和成本管理
    # 3. 支持新能源车辆和充电站规划
    # 4. 建立司机技能匹配和绩效评估
    # 5. 实现车辆实时监控和异常预警
    # 技术实现:
    # 1. 扩展车辆属性支持燃油类型、排放标准
    # 2. 集成车载设备实现实时数据采集
    # 3. 开发车辆调度优化算法
    # 4. 建立车辆生命周期管理系统

@dataclass
class DeliveryOrder:
    """配送订单信息"""
    order_id: str
    customer_id: str
    pickup_location: str
    delivery_location: str
    weight: float
    volume: float
    priority: int  # 优先级(1-5, 5最高)
    time_window_start: datetime
    time_window_end: datetime
    estimated_service_time: int  # 预计服务时间(分钟)
    special_requirements: List[str]
    status: str = DeliveryStatus.PENDING.value
    assigned_vehicle: str = ""
    actual_delivery_time: Optional[datetime] = None
    
    # TODO: [智能订单管理] - 实现订单优化和客户体验提升
    # 业务需求:
    # 1. 建立订单优先级动态调整机制
    # 2. 实现订单合并和拆分优化
    # 3. 支持客户偏好和配送时间预测
    # 4. 建立订单异常处理和补救措施
    # 5. 实现订单成本和利润分析
    # 技术实现:
    # 1. 开发订单聚类和批次优化算法
    # 2. 建立客户画像和行为预测模型
    # 3. 实现订单状态实时同步和通知
    # 4. 集成客户反馈和满意度评估

@dataclass
class Route:
    """配送路线"""
    route_id: str
    vehicle_id: str
    driver_id: str
    orders: List[str]  # 订单ID列表
    locations: List[str]  # 位置ID列表(按访问顺序)
    total_distance: float
    total_time: int  # 总时间(分钟)
    total_cost: float
    estimated_start_time: datetime
    estimated_end_time: datetime
    actual_start_time: Optional[datetime] = None
    
    # TODO: [智能路径规划] - 实现多目标路径优化和动态调整
    # 业务需求:
    # 1. 支持多目标优化（时间、成本、客户满意度、环保）
    # 2. 实现实时路径调整和重新规划
    # 3. 建立路径学习和历史优化
    # 4. 支持紧急订单插入和路径重组
    # 5. 实现路径风险评估和备选方案
    # 技术实现:
    # 1. 使用遗传算法、模拟退火等元启发式算法
    # 2. 集成实时交通数据和路况预测
    # 3. 开发路径评估和比较系统
    # 4. 建立路径执行监控和偏差分析
    actual_end_time: Optional[datetime] = None
    status: str = "planned"

class DeliveryRouteOptimizer:
    """配送路径优化系统核心类"""
    
    def __init__(self):
        self.locations: Dict[str, Location] = {}
        self.vehicles: Dict[str, Vehicle] = {}
        self.orders: Dict[str, DeliveryOrder] = {}
        self.routes: Dict[str, Route] = {}
        self.distance_matrix: Dict[Tuple[str, str], float] = {}
        self.traffic_data: Dict[Tuple[str, str], Dict] = {}
        
    def add_location(self, location: Location) -> bool:
        """添加位置信息"""
        try:
            self.locations[location.id] = location
            logger.info(f"位置 {location.name} 添加成功")
            return True
        except Exception as e:
            logger.error(f"添加位置失败: {e}")
            return False
    
    def register_vehicle(self, vehicle: Vehicle) -> bool:
        """注册配送车辆"""
        try:
            self.vehicles[vehicle.vehicle_id] = vehicle
            logger.info(f"车辆 {vehicle.vehicle_id} 注册成功")
            return True
        except Exception as e:
            logger.error(f"车辆注册失败: {e}")
            return False
    
    def add_delivery_order(self, order: DeliveryOrder) -> bool:
        """添加配送订单"""
        try:
            self.orders[order.order_id] = order
            logger.info(f"配送订单 {order.order_id} 添加成功")
            return True
        except Exception as e:
            logger.error(f"添加配送订单失败: {e}")
            return False
    
    def calculate_distance(self, loc1_id: str, loc2_id: str) -> float:
        """计算两点间距离(使用哈弗辛公式)"""
        if (loc1_id, loc2_id) in self.distance_matrix:
            return self.distance_matrix[(loc1_id, loc2_id)]
        
        if loc1_id not in self.locations or loc2_id not in self.locations:
            return float('inf')
        
        loc1 = self.locations[loc1_id]
        loc2 = self.locations[loc2_id]
        
        # 哈弗辛公式计算距离
        R = 6371  # 地球半径(km)
        lat1, lon1 = math.radians(loc1.latitude), math.radians(loc1.longitude)
        lat2, lon2 = math.radians(loc2.latitude), math.radians(loc2.longitude)
        
        dlat = lat2 - lat1
        dlon = lon2 - lon1
        
        a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
        c = 2 * math.asin(math.sqrt(a))
        distance = R * c
        
        # 缓存计算结果
        self.distance_matrix[(loc1_id, loc2_id)] = distance
        self.distance_matrix[(loc2_id, loc1_id)] = distance
        
        return distance
    
    def get_travel_time(self, loc1_id: str, loc2_id: str, departure_time: datetime = None) -> int:
        """获取行驶时间(分钟)，考虑交通状况"""
        distance = self.calculate_distance(loc1_id, loc2_id)
        if distance == float('inf'):
            return float('inf')
        
        # 基础速度(km/h)
        base_speed = 40
        
        # 根据时间调整速度(模拟交通状况)
        if departure_time:
            hour = departure_time.hour
            if 7 <= hour <= 9 or 17 <= hour <= 19:  # 高峰期
                speed_factor = 0.6
            elif 22 <= hour or hour <= 6:  # 夜间
                speed_factor = 1.2
            else:  # 平峰期
                speed_factor = 1.0
        else:
            speed_factor = 1.0
        
        actual_speed = base_speed * speed_factor
        travel_time = (distance / actual_speed) * 60  # 转换为分钟
        
        return int(travel_time)
    
    def check_vehicle_constraints(self, vehicle_id: str, orders: List[str]) -> bool:
        """检查车辆约束条件"""
        if vehicle_id not in self.vehicles:
            return False
        
        vehicle = self.vehicles[vehicle_id]
        total_weight = 0
        total_volume = 0
        
        for order_id in orders:
            if order_id not in self.orders:
                return False
            order = self.orders[order_id]
            total_weight += order.weight
            total_volume += order.volume
        
        return (total_weight <= vehicle.capacity_weight and 
                total_volume <= vehicle.capacity_volume)
    
    def calculate_route_cost(self, vehicle_id: str, locations: List[str], 
                           start_time: datetime = None) -> Tuple[float, int, float]:
        """计算路线成本、时间和距离"""
        if not locations or vehicle_id not in self.vehicles:
            return float('inf'), float('inf'), float('inf')
        
        vehicle = self.vehicles[vehicle_id]
        total_distance = 0
        total_time = 0
        current_time = start_time or datetime.now()
        
        # 从车辆当前位置开始
        current_location = vehicle.current_location
        
        for next_location in locations:
            distance = self.calculate_distance(current_location, next_location)
            travel_time = self.get_travel_time(current_location, next_location, current_time)
            
            if distance == float('inf') or travel_time == float('inf'):
                return float('inf'), float('inf'), float('inf')
            
            total_distance += distance
            total_time += travel_time
            
            # 添加服务时间
            if next_location in [order.delivery_location for order in self.orders.values()]:
                order = next((o for o in self.orders.values() 
                            if o.delivery_location == next_location), None)
                if order:
                    total_time += order.estimated_service_time
            
            current_location = next_location
            current_time += timedelta(minutes=travel_time)
        
        # 返回仓库
        warehouse_locations = [loc.id for loc in self.locations.values() if loc.is_warehouse]
        if warehouse_locations:
            return_distance = self.calculate_distance(current_location, warehouse_locations[0])
            return_time = self.get_travel_time(current_location, warehouse_locations[0], current_time)
            total_distance += return_distance
            total_time += return_time
        
        total_cost = total_distance * vehicle.cost_per_km
        
        return total_cost, total_time, total_distance
    
    def optimize_single_route(self, vehicle_id: str, order_ids: List[str]) -> Optional[List[str]]:
        """优化单条路线(使用贪心算法)"""
        if not order_ids or vehicle_id not in self.vehicles:
            return None
        
        # 检查车辆约束
        if not self.check_vehicle_constraints(vehicle_id, order_ids):
            return None
        
        vehicle = self.vehicles[vehicle_id]
        unvisited = order_ids.copy()
        route = []
        current_location = vehicle.current_location
        
        # 贪心算法：每次选择最近的未访问订单
        while unvisited:
            best_order = None
            best_distance = float('inf')
            
            for order_id in unvisited:
                order = self.orders[order_id]
                # 先到取货点，再到送货点
                pickup_distance = self.calculate_distance(current_location, order.pickup_location)
                delivery_distance = self.calculate_distance(order.pickup_location, order.delivery_location)
                total_distance = pickup_distance + delivery_distance
                
                if total_distance < best_distance:
                    best_distance = total_distance
                    best_order = order_id
            
            if best_order:
                order = self.orders[best_order]
                route.extend([order.pickup_location, order.delivery_location])
                current_location = order.delivery_location
                unvisited.remove(best_order)
        
        return route
    
    def optimize_multiple_routes(self, max_routes: int = None) -> Dict[str, List[str]]:
        """优化多条路线分配"""
        # 获取待分配的订单
        pending_orders = [order_id for order_id, order in self.orders.items() 
                         if order.status == DeliveryStatus.PENDING.value]
        
        if not pending_orders:
            return {}
        
        # 获取可用车辆
        available_vehicles = [v_id for v_id, vehicle in self.vehicles.items() 
                            if vehicle.is_available]
        
        if not available_vehicles:
            logger.warning("没有可用车辆")
            return {}
        
        # 按优先级排序订单
        pending_orders.sort(key=lambda x: self.orders[x].priority, reverse=True)
        
        # 简单的分配策略：轮询分配
        routes = {}
        vehicle_loads = {v_id: [] for v_id in available_vehicles}
        
        for order_id in pending_orders:
            order = self.orders[order_id]
            best_vehicle = None
            best_cost = float('inf')
            
            for vehicle_id in available_vehicles:
                # 检查是否可以添加到当前车辆
                test_orders = vehicle_loads[vehicle_id] + [order_id]
                if self.check_vehicle_constraints(vehicle_id, test_orders):
                    # 计算添加此订单后的成本增加
                    current_route = self.optimize_single_route(vehicle_id, vehicle_loads[vehicle_id])
                    new_route = self.optimize_single_route(vehicle_id, test_orders)
                    
                    if new_route:  # 只要新路线有效即可
                        if current_route:
                            current_cost, _, _ = self.calculate_route_cost(vehicle_id, current_route)
                            new_cost, _, _ = self.calculate_route_cost(vehicle_id, new_route)
                            cost_increase = new_cost - current_cost
                        else:
                            # 空载车辆，直接使用新路线成本
                            cost_increase, _, _ = self.calculate_route_cost(vehicle_id, new_route)
                        
                        if cost_increase < best_cost:
                            best_cost = cost_increase
                            best_vehicle = vehicle_id
            
            if best_vehicle:
                vehicle_loads[best_vehicle].append(order_id)
        
        # 为每个有订单的车辆生成优化路线
        for vehicle_id, order_list in vehicle_loads.items():
            if order_list:
                optimized_route = self.optimize_single_route(vehicle_id, order_list)
                if optimized_route:
                    routes[vehicle_id] = optimized_route
        
        return routes
    
    def create_delivery_routes(self, start_time: datetime = None) -> List[str]:
        """创建配送路线"""
        if start_time is None:
            start_time = datetime.now()
        
        # 获取待分配的订单
        pending_orders = [order_id for order_id, order in self.orders.items() 
                         if order.status == DeliveryStatus.PENDING.value]
        
        if not pending_orders:
            return []
        
        # 获取可用车辆
        available_vehicles = [v_id for v_id, vehicle in self.vehicles.items() 
                            if vehicle.is_available]
        
        if not available_vehicles:
            logger.warning("没有可用车辆")
            return []
        
        # 按优先级排序订单
        pending_orders.sort(key=lambda x: self.orders[x].priority, reverse=True)
        
        # 简单的分配策略：轮询分配
        vehicle_loads = {v_id: [] for v_id in available_vehicles}
        
        for order_id in pending_orders:
            order = self.orders[order_id]
            best_vehicle = None
            best_cost = float('inf')
            
            for vehicle_id in available_vehicles:
                # 检查是否可以添加到当前车辆
                test_orders = vehicle_loads[vehicle_id] + [order_id]
                if self.check_vehicle_constraints(vehicle_id, test_orders):
                    # 计算添加此订单后的成本增加
                    current_route = self.optimize_single_route(vehicle_id, vehicle_loads[vehicle_id])
                    new_route = self.optimize_single_route(vehicle_id, test_orders)
                    
                    if new_route:  # 只要新路线有效即可
                        if current_route:
                            current_cost, _, _ = self.calculate_route_cost(vehicle_id, current_route)
                            new_cost, _, _ = self.calculate_route_cost(vehicle_id, new_route)
                            cost_increase = new_cost - current_cost
                        else:
                            # 空载车辆，直接使用新路线成本
                            cost_increase, _, _ = self.calculate_route_cost(vehicle_id, new_route)
                        
                        if cost_increase < best_cost:
                            best_cost = cost_increase
                            best_vehicle = vehicle_id
            
            if best_vehicle:
                vehicle_loads[best_vehicle].append(order_id)
        
        created_routes = []
        
        # 为每个有订单的车辆生成优化路线
        for vehicle_id, order_list in vehicle_loads.items():
            if not order_list:
                continue
                
            # 生成优化路线
            locations = self.optimize_single_route(vehicle_id, order_list)
            if not locations:
                continue
            
            # 计算路线信息
            total_cost, total_time, total_distance = self.calculate_route_cost(
                vehicle_id, locations, start_time
            )
            
            if total_cost == float('inf'):
                continue
            
            # 创建路线对象
            route_id = f"ROUTE_{vehicle_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
            route = Route(
                route_id=route_id,
                vehicle_id=vehicle_id,
                driver_id=self.vehicles[vehicle_id].driver_id,
                orders=order_list,
                locations=locations,
                total_distance=total_distance,
                total_time=total_time,
                total_cost=total_cost,
                estimated_start_time=start_time,
                estimated_end_time=start_time + timedelta(minutes=total_time)
            )
            
            self.routes[route_id] = route
            created_routes.append(route_id)
            
            # 更新订单状态
            for order_id in order_list:
                self.orders[order_id].status = DeliveryStatus.ASSIGNED.value
                self.orders[order_id].assigned_vehicle = vehicle_id
            
            # 更新车辆状态
            self.vehicles[vehicle_id].is_available = False
            
            logger.info(f"路线 {route_id} 创建成功，包含 {len(order_list)} 个订单")
        
        return created_routes
    
    def update_route_status(self, route_id: str, status: str, 
                          current_location: str = "") -> bool:
        """更新路线状态"""
        if route_id not in self.routes:
            return False
        
        route = self.routes[route_id]
        route.status = status
        
        if status == "started":
            route.actual_start_time = datetime.now()
        elif status == "completed":
            route.actual_end_time = datetime.now()
            # 释放车辆
            self.vehicles[route.vehicle_id].is_available = True
            # 更新订单状态
            for order_id in route.orders:
                self.orders[order_id].status = DeliveryStatus.DELIVERED.value
                self.orders[order_id].actual_delivery_time = datetime.now()
        
        logger.info(f"路线 {route_id} 状态更新为 {status}")
        return True
    
    def get_driver_workload(self, driver_id: str, date: datetime = None) -> Dict:
        """获取司机工作负载"""
        if date is None:
            date = datetime.now().date()
        
        driver_routes = [route for route in self.routes.values() 
                        if (route.driver_id == driver_id and 
                            route.estimated_start_time.date() == date)]
        
        if not driver_routes:
            return {
                'driver_id': driver_id,
                'date': date.isoformat(),
                'total_routes': 0,
                'total_orders': 0,
                'total_distance': 0,
                'total_time': 0,
                'workload_percentage': 0
            }
        
        total_orders = sum(len(route.orders) for route in driver_routes)
        total_distance = sum(route.total_distance for route in driver_routes)
        total_time = sum(route.total_time for route in driver_routes)
        
        # 假设标准工作时间为8小时(480分钟)
        standard_work_time = 480
        workload_percentage = min(100, (total_time / standard_work_time) * 100)
        
        return {
            'driver_id': driver_id,
            'date': date.isoformat(),
            'total_routes': len(driver_routes),
            'total_orders': total_orders,
            'total_distance': round(total_distance, 2),
            'total_time': total_time,
            'workload_percentage': round(workload_percentage, 2)
        }
    
    def get_delivery_efficiency_report(self, start_date: datetime = None, 
                                     end_date: datetime = None) -> Dict:
        """获取配送效率分析报告"""
        if start_date is None:
            start_date = datetime.now() - timedelta(days=7)
        if end_date is None:
            end_date = datetime.now()
        
        # 筛选时间范围内的路线
        period_routes = [route for route in self.routes.values() 
                        if start_date <= route.estimated_start_time <= end_date]
        
        if not period_routes:
            return {'message': '指定时间范围内无配送数据'}
        
        # 计算各项指标
        total_routes = len(period_routes)
        total_orders = sum(len(route.orders) for route in period_routes)
        total_distance = sum(route.total_distance for route in period_routes)
        total_cost = sum(route.total_cost for route in period_routes)
        
        # 按时完成率
        completed_routes = [r for r in period_routes if r.status == "completed"]
        on_time_routes = [r for r in completed_routes 
                         if (r.actual_end_time and 
                             r.actual_end_time <= r.estimated_end_time)]
        
        on_time_rate = (len(on_time_routes) / len(completed_routes) * 100) if completed_routes else 0
        
        # 车辆利用率
        vehicle_usage = {}
        for route in period_routes:
            vehicle_id = route.vehicle_id
            if vehicle_id not in vehicle_usage:
                vehicle_usage[vehicle_id] = {'routes': 0, 'distance': 0, 'time': 0}
            vehicle_usage[vehicle_id]['routes'] += 1
            vehicle_usage[vehicle_id]['distance'] += route.total_distance
            vehicle_usage[vehicle_id]['time'] += route.total_time
        
        avg_orders_per_route = total_orders / total_routes if total_routes > 0 else 0
        avg_distance_per_route = total_distance / total_routes if total_routes > 0 else 0
        avg_cost_per_order = total_cost / total_orders if total_orders > 0 else 0
        
        return {
            'period': {
                'start_date': start_date.isoformat(),
                'end_date': end_date.isoformat()
            },
            'summary': {
                'total_routes': total_routes,
                'total_orders': total_orders,
                'total_distance': round(total_distance, 2),
                'total_cost': round(total_cost, 2),
                'completed_routes': len(completed_routes),
                'on_time_rate': round(on_time_rate, 2)
            },
            'efficiency': {
                'avg_orders_per_route': round(avg_orders_per_route, 2),
                'avg_distance_per_route': round(avg_distance_per_route, 2),
                'avg_cost_per_order': round(avg_cost_per_order, 2)
            },
            'vehicle_usage': vehicle_usage
        }
    
    def export_routes_data(self, file_path: str) -> bool:
        """导出路线数据"""
        try:
            routes_data = []
            for route in self.routes.values():
                data = asdict(route)
                # 转换datetime对象为字符串
                for key, value in data.items():
                    if isinstance(value, datetime):
                        data[key] = value.isoformat()
                routes_data.append(data)
            
            df = pd.DataFrame(routes_data)
            df.to_csv(file_path, index=False, encoding='utf-8-sig')
            logger.info(f"路线数据已导出到 {file_path}")
            return True
        except Exception as e:
            logger.error(f"导出数据失败: {e}")
            return False

# 示例使用
if __name__ == "__main__":
    # 创建配送路径优化系统实例
    optimizer = DeliveryRouteOptimizer()
    
    # 添加位置
    locations = [
        Location("W001", "中央仓库", 39.9042, 116.4074, "北京市朝阳区", True),
        Location("C001", "客户A", 39.9100, 116.4200, "北京市东城区"),
        Location("C002", "客户B", 39.8900, 116.3900, "北京市西城区"),
        Location("C003", "客户C", 39.9200, 116.4300, "北京市朝阳区")
    ]
    
    for loc in locations:
        optimizer.add_location(loc)
    
    # 注册车辆
    vehicles = [
        Vehicle("V001", VehicleType.VAN.value, 1000, 10, 200, 1.5, "D001", "W001"),
        Vehicle("V002", VehicleType.TRUCK.value, 2000, 20, 300, 2.0, "D002", "W001")
    ]
    
    for vehicle in vehicles:
        optimizer.register_vehicle(vehicle)
    
    # 添加配送订单
    orders = [
        DeliveryOrder(
            "O001", "CUST001", "W001", "C001", 50, 2,
            3, datetime.now() + timedelta(hours=1),
            datetime.now() + timedelta(hours=4), 15, []
        ),
        DeliveryOrder(
            "O002", "CUST002", "W001", "C002", 30, 1.5,
            2, datetime.now() + timedelta(hours=2),
            datetime.now() + timedelta(hours=5), 10, []
        )
    ]
    
    for order in orders:
        optimizer.add_delivery_order(order)
    
    # 创建配送路线
    route_ids = optimizer.create_delivery_routes()
    print(f"创建了 {len(route_ids)} 条配送路线")
    
    # 获取配送效率报告
    efficiency_report = optimizer.get_delivery_efficiency_report()
    print("配送效率报告:", json.dumps(efficiency_report, indent=2, default=str, ensure_ascii=False))
    
    # 获取司机工作负载
    workload = optimizer.get_driver_workload("D001")
    print("司机工作负载:", json.dumps(workload, indent=2, ensure_ascii=False))